/*
 *
 */
package main;

import gui.GrapgView;

import java.io.IOException;

import org.jfree.ui.RefineryUtilities;

import mutation.MutationManager;

/**
 * A single run of the GA algorithm.
 */
public class GARun extends GAabstruct {

	/**
	 * the graph that represent's the system
	 * 
	 */
	private GrapgView graph;

	/**
	 * constructor : creates a new GARun
	 * 
	 * @param fileWriter
	 *            the fileWriter of the system to write the result of the run
	 *            when finised
	 * @param config
	 *            the configuration file of the system
	 * @param mutationManager
	 *            the mutation manager of the system
	 */
	public GARun(OutputFilesWriter fileWriter, Configuration config,
			MutationManager mutationManager) {

		super(fileWriter, config, mutationManager);
		initGraph();
	}

	/*
	 * create new random population
	 * 
	 * @param runNumber
	 * 
	 * public void initiate(int runNumber){
	 * 
	 * System.out.println("Initializing run # "+runNumber);
	 * setId("runNumber "+runNumber);
	 * 
	 * graph.setTitle(getId());
	 * 
	 * 
	 * population = new Population(config, random, mutationManager);
	 * Collections.sort(population, Collections.reverseOrder());
	 * 
	 * population.sort(); currentGenerarionNum = 0; if (isFirstInit){ //if this
	 * is the first init then create the needed arrays else. clear the existing
	 * one's population.sort(); //Collections.sort(population,
	 * Collections.reverseOrder()); bestEnergy = new
	 * float[numberOfGenerations/reportEvery+1]; averageEnergy = new
	 * float[numberOfGenerations/reportEvery+1]; worstEnergy = new
	 * float[numberOfGenerations/reportEvery+1]; bestFitness = new
	 * float[numberOfGenerations/reportEvery+1]; averageFitness = new
	 * float[numberOfGenerations/reportEvery+1]; worstFitness = new
	 * float[numberOfGenerations/reportEvery+1]; fittest = new
	 * Conformation[numberOfGenerations/reportEvery+1]; generation = new
	 * int[numberOfGenerations/reportEvery+1]; } for (int
	 * i=0;i<numberOfGenerations/config.reportEvery+1;i++){ bestEnergy[i] = 0;
	 * averageEnergy[i] = 0; worstEnergy[i] = 0; bestFitness[i] = 0;
	 * averageFitness[i] = 0; worstFitness[i] = 0; fittest[i] = null; } }
	 */

	/**
	 * creates a new graph and add it to the system
	 */
	private void initGraph() {
		// final double[] a={-50,-10,5,-13,7,80,90};
		// GrafhOutput go=new GrafhOutput(a, 1000);
		graph = new GrapgView("details");
		graph.pack();
		RefineryUtilities.centerFrameOnScreen(graph);
		graph.setVisible(true);

	}

	/**
	 * add a coordinate of (genNum,BestEnergyVal) to the graph
	 * 
	 * @param genNum
	 *            the current generation of this update
	 * @param bestEnergyVal
	 *            the best individual in this generation
	 */
	public void updateGraph(int genNum, double bestEnergyVal) {
		graph.getDataSet().addValue(bestEnergyVal, getId(),
				Integer.toString(genNum));
	}

	/**
	 * Execute the run.
	 * 
	 * @see GAinterface
	 */
	@Override
	public void execute() {
		Protein in1, in2, out1, out2;
		Protein[] temp;

		long startTime = System.currentTimeMillis();
		long runningTime;

		for (currentGenerarionNum = 0; currentGenerarionNum < config.numberOfGenerations; currentGenerarionNum++) {
			// Collections.sort(population, Collections.reverseOrder());
			population.sort();
			Protein best = population.getFirst();
			if (currentGenerarionNum % config.reportEvery == 0) {
				bestEnergy[currentGenerarionNum / reportEvery] = population
						.getBestEnergy();
				averageEnergy[currentGenerarionNum / reportEvery] = population
						.getAverageEnergy();
				worstEnergy[currentGenerarionNum / reportEvery] = population
						.getWorstEnergy();
				fittest[currentGenerarionNum / reportEvery] = best.conformation;
				averageFitness[currentGenerarionNum / reportEvery] = population
						.getAverageFitness();
				worstFitness[currentGenerarionNum / reportEvery] = population
						.getLast().getFitness();
				bestFitness[currentGenerarionNum / reportEvery] = best
						.getFitness();
				generation[currentGenerarionNum / reportEvery] = currentGenerarionNum;
			}

			if (random.nextFloat() < config.crossoverRate) {
				in1 = population.chooseProtein();
				in2 = population.chooseProtein();
				temp = population.getLastTwo();
				out1 = temp[0];
				out2 = temp[1];
				Protein.crossover(in1, in2, out1, out2, random);
				population.updateLastTwo();

			} else {
				in1 = population.chooseProtein();
				out1 = population.getLast();
				mutationManager.mutate(in1, out1, 10);
				population.updateLastTwo();
			}

			boolean changed = false;
			if (SHOW_BEST_IN_GUI
					&& (this.bestProtein == null || best.getEnergy() < this.bestProtein
							.getEnergy())) {
				this.bestProtein.reset();
				this.bestProtein.setConformation(best.conformation);
				// this.bestProtein=new Protein(best);
				changed = true;

			}

			runningTime = (System.currentTimeMillis() - startTime);

			/**
			 * we will update the graph in 10 x coordinate
			 */
			if (currentGenerarionNum % (config.numberOfGenerations / 10) == 0) {
				updateGraph(currentGenerarionNum, best.getEnergy());
			}
			if (currentGenerarionNum % config.reportEvery == 0) {
				// updateGraph(currentGenerarionNum,best.getEnergy());

				System.out.println("Generation  " + currentGenerarionNum
						+ "  out of   " + config.numberOfGenerations
						+ "  Time is:  " + runningTime
						+ "  msec.      Fittest \n" + population.getFirst());
				gui.buildCurrentGrid(best);
				gui.setCurrentLabelDetails("energy="
						+ Double.toString(best.getEnergy()) + " , Genaration="
						+ currentGenerarionNum);

				// guiGrid.run();
			}
			if (SHOW_BEST_IN_GUI && changed) {
				gui.buildBestGrid(this.bestProtein);

				gui.setBestLabelDetails("best protain so far: " + getId()
						+ ", energy="
						+ Double.toString(this.bestProtein.getEnergy())
						+ " , Genaration=" + currentGenerarionNum);

				// bestguiGrid.run();
			}
		}
		// write results after run
		try {
			fileWriter.writeResults(this);
		} catch (IOException ex) {
			throw new RuntimeException("Failed to write output file\n" + ex);
		}
	}

}
